//---------------------------------------------------------------------------
//
// Copyright (C) Microsoft Corporation.  All rights reserved.
//
// File: RibbonTab.cs
//
//---------------------------------------------------------------------------

#region Using declarations

using System;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Windows;
using System.Windows.Automation.Peers;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using Microsoft.Windows.Automation.Peers;
using Microsoft.Windows.Controls.Ribbon.Primitives;


#endregion

namespace Microsoft.Windows.Controls.Ribbon
{
	[StyleTypedProperty(Property = "HeaderStyle", StyleTargetType = typeof(RibbonTabHeader))]
	public class RibbonTab : HeaderedItemsControl
	{
		#region Constructor

		static RibbonTab()
		{
			Type ownerType = typeof(RibbonTab);

			IsEnabledProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(new PropertyChangedCallback(OnIsEnabledChanged)));
			DefaultStyleKeyProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(ownerType));
			ItemsPanelProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(new ItemsPanelTemplate(new FrameworkElementFactory(typeof(RibbonGroupsPanel)))));
			HeaderProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(new PropertyChangedCallback(OnHeaderChanged)));
			VisibilityProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(Visibility.Visible, new PropertyChangedCallback(OnVisibilityChanged), new CoerceValueCallback(CoerceVisibility)));
			HeaderTemplateProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(new PropertyChangedCallback(OnNotifyHeaderPropertyChanged), new CoerceValueCallback(CoerceHeaderTemplate)));
			HeaderTemplateSelectorProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(new PropertyChangedCallback(OnNotifyHeaderPropertyChanged)));
			HeaderStringFormatProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(new PropertyChangedCallback(OnNotifyHeaderPropertyChanged)));
			FocusableProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(false));
			EventManager.RegisterClassHandler(ownerType, KeyTipService.ActivatingKeyTipEvent, new ActivatingKeyTipEventHandler(OnActivatingKeyTipThunk));
			EventManager.RegisterClassHandler(ownerType, KeyTipService.KeyTipAccessedEvent, new KeyTipAccessedEventHandler(OnKeyTipAccessedThunk));
			KeyTipService.KeyTipProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(new PropertyChangedCallback(OnKeyTipChanged)));
		}

		#endregion

		#region Properties

		protected override bool HandlesScrolling
		{
			get
			{
				return true;
			}
		}

		/// <summary>
		///     The name collection representing the order in which groups should be reduced.
		/// </summary>
		[TypeConverter(typeof(StringCollectionConverter))]
		public StringCollection GroupSizeReductionOrder
		{
			get { return (StringCollection)GetValue(GroupSizeReductionOrderProperty); }
			set { SetValue(GroupSizeReductionOrderProperty, value); }
		}

		/// <summary>
		///     Dependency property backing GroupSizeReductionOrder
		/// </summary>
		public static readonly DependencyProperty GroupSizeReductionOrderProperty =
				DependencyProperty.Register(
							"GroupSizeReductionOrder",
							typeof(StringCollection),
							typeof(RibbonTab),
							new FrameworkPropertyMetadata(null));

		/// <summary>
		///     Boolean indicating whether this RibbonTab is selected
		/// </summary>
		public bool IsSelected
		{
			get { return (bool)GetValue(IsSelectedProperty); }
			set { SetValue(IsSelectedProperty, value); }
		}

		/// <summary>
		///     Dependency property backing IsSelected
		/// </summary>
		public static readonly DependencyProperty IsSelectedProperty =
				Selector.IsSelectedProperty.AddOwner(typeof(RibbonTab),
						new FrameworkPropertyMetadata(false,
								FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.AffectsParentMeasure | FrameworkPropertyMetadataOptions.Journal,
								new PropertyChangedCallback(OnIsSelectedChanged)));

		/// <summary>
		///     Name of the ContextualTabGroupHeader to which this RibbonTab belongs.
		/// </summary>
		public object ContextualTabGroupHeader
		{
			get { return GetValue(ContextualTabGroupHeaderProperty); }
			set { SetValue(ContextualTabGroupHeaderProperty, value); }
		}

		/// <summary>
		///     Using a DependencyProperty as the backing store for ContextualTabGroupHeader.  This enables animation, styling, binding, etc...
		/// </summary>
		public static readonly DependencyProperty ContextualTabGroupHeaderProperty =
			DependencyProperty.Register("ContextualTabGroupHeader", typeof(object), typeof(RibbonTab), new UIPropertyMetadata(null, new PropertyChangedCallback(OnContextualTabGroupHeaderChanged)));

		private static readonly DependencyPropertyKey ContextualTabGroupPropertyKey =
			DependencyProperty.RegisterReadOnly("ContextualTabGroup", typeof(RibbonContextualTabGroup), typeof(RibbonTab), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnNotifyHeaderPropertyChanged)));

		public static readonly DependencyProperty ContextualTabGroupProperty = ContextualTabGroupPropertyKey.DependencyProperty;

		public RibbonContextualTabGroup ContextualTabGroup
		{
			get { return (RibbonContextualTabGroup)GetValue(ContextualTabGroupProperty); }
			internal set { SetValue(ContextualTabGroupPropertyKey, value); }
		}

		public Style HeaderStyle
		{
			get { return (Style)GetValue(HeaderStyleProperty); }
			set { SetValue(HeaderStyleProperty, value); }
		}

		// Using a DependencyProperty as the backing store for HeaderStyle.  This enables animation, styling, binding, etc...
		public static readonly DependencyProperty HeaderStyleProperty =
			DependencyProperty.Register("HeaderStyle", typeof(Style), typeof(RibbonTab), new FrameworkPropertyMetadata(null, OnNotifyHeaderPropertyChanged, CoerceHeaderStyle));

		/// <summary>
		///     DependencyProperty for Ribbon property.
		/// </summary>
		public static readonly DependencyProperty RibbonProperty =
			RibbonControlService.RibbonProperty.AddOwner(typeof(RibbonTab));

		/// <summary>
		///     This property is used to access Ribbon
		/// </summary>
		public Ribbon Ribbon
		{
			get { return RibbonControlService.GetRibbon(this); }
		}

		/// <summary>
		///     Boolean indicating if this RibbonTab is a contextual tab.
		/// </summary>
		internal bool IsContextualTab
		{
			get
			{
				return ContextualTabGroupHeader != null;
			}
		}

		/// <summary>
		///     Property which returns the corresponding RibbonTabHeader for this RibbonTab
		/// </summary>
		internal RibbonTabHeader RibbonTabHeader
		{
			get
			{
				Ribbon ribbon = Ribbon;
				if (ribbon != null)
				{
					int index = ribbon.ItemContainerGenerator.IndexFromContainer(this);
					if (index >= 0)
					{
						RibbonTabHeaderItemsControl headerItemsControl = ribbon.RibbonTabHeaderItemsControl;
						if (headerItemsControl != null)
						{
							return headerItemsControl.ItemContainerGenerator.ContainerFromIndex(index) as RibbonTabHeader;
						}
					}
				}
				return null;
			}
		}

		private static readonly DependencyPropertyKey TabHeaderLeftPropertyKey =
			DependencyProperty.RegisterReadOnly("TabHeaderLeft", typeof(double), typeof(RibbonTab), null);

		public static readonly DependencyProperty TabHeaderLeftProperty = TabHeaderLeftPropertyKey.DependencyProperty;

		/// <summary>
		///     This is position of the left edge of the corresponding RibbonTabHeader in the coordinate space of this RibbonTab
		/// </summary>
		public double TabHeaderLeft
		{
			get { return (double)GetValue(TabHeaderLeftProperty); }
			internal set { SetValue(TabHeaderLeftPropertyKey, value); }
		}

		private static readonly DependencyPropertyKey TabHeaderRightPropertyKey =
			DependencyProperty.RegisterReadOnly("TabHeaderRight", typeof(double), typeof(RibbonTab), null);

		public static readonly DependencyProperty TabHeaderRightProperty = TabHeaderRightPropertyKey.DependencyProperty;

		/// <summary>
		///     This is position of the right edge of the corresponding RibbonTabHeader in the coordinate space of this RibbonTab
		/// </summary>
		public double TabHeaderRight
		{
			get { return (double)GetValue(TabHeaderRightProperty); }
			internal set { SetValue(TabHeaderRightPropertyKey, value); }
		}

		#endregion

		#region Protected Methods

		/// <summary>
		///     Returns a new RibbonGroup as the item container
		/// </summary>
		protected override DependencyObject GetContainerForItemOverride()
		{
			return new RibbonGroup();
		}

		/// <summary>
		///     An item is its own container if it is RibbonGroup.
		/// </summary>
		protected override bool IsItemItsOwnContainerOverride(object item)
		{
			return (item is RibbonGroup);
		}

		protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
		{
			ItemsControl childItemsControl = element as ItemsControl;
			if (childItemsControl != null)
			{
				// copy templates and styles from this ItemsControl
				var itemTemplate = RibbonHelper.GetValueAndValueSource(childItemsControl, ItemsControl.ItemTemplateProperty);
				var itemTemplateSelector = RibbonHelper.GetValueAndValueSource(childItemsControl, ItemsControl.ItemTemplateSelectorProperty);
				var itemStringFormat = RibbonHelper.GetValueAndValueSource(childItemsControl, ItemsControl.ItemStringFormatProperty);
				var itemContainerStyle = RibbonHelper.GetValueAndValueSource(childItemsControl, ItemsControl.ItemContainerStyleProperty);
				var itemContainerStyleSelector = RibbonHelper.GetValueAndValueSource(childItemsControl, ItemsControl.ItemContainerStyleSelectorProperty);
				var alternationCount = RibbonHelper.GetValueAndValueSource(childItemsControl, ItemsControl.AlternationCountProperty);
				var itemBindingGroup = RibbonHelper.GetValueAndValueSource(childItemsControl, ItemsControl.ItemBindingGroupProperty);

				base.PrepareContainerForItemOverride(element, item);

				// Call this function to work around a restriction of supporting hetrogenous 
				// ItemsCotnrol hierarchy. The method takes care of both ItemsControl and
				// HeaderedItemsControl (in this case) and assign back the default properties
				// whereever appropriate.
				RibbonHelper.IgnoreDPInheritedFromParentItemsControl(
					childItemsControl,
					this,
					itemTemplate,
					itemTemplateSelector,
					itemStringFormat,
					itemContainerStyle,
					itemContainerStyleSelector,
					alternationCount,
					itemBindingGroup,
					null,
					null,
					null);
			}
			else
			{
				base.PrepareContainerForItemOverride(element, item);
			}

			RibbonGroup ribbonGroup = element as RibbonGroup;
			if (ribbonGroup != null)
			{
				ribbonGroup.PrepareRibbonGroup();
			}
		}

		protected override void ClearContainerForItemOverride(DependencyObject element, object item)
		{
			base.ClearContainerForItemOverride(element, item);
			RibbonGroup ribbonGroup = element as RibbonGroup;
			if (ribbonGroup != null)
			{
				ribbonGroup.ClearRibbonGroup();
			}
		}

		/// <summary>
		///     Raises event indicating that the IsSelected property is now true.
		/// </summary>
		/// <param name="e">Event arguments</param>
		protected virtual void OnSelected(RoutedEventArgs e)
		{
			RaiseEvent(e);
		}

		/// <summary>
		///     Raises event indicating that the IsSelected property is now false.
		/// </summary>
		/// <param name="e">Event arguments</param>
		protected virtual void OnUnselected(RoutedEventArgs e)
		{
			RaiseEvent(e);
		}

		protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
		{
			base.OnItemsChanged(e);

			switch (e.Action)
			{
				case NotifyCollectionChangedAction.Reset:
					_groupAutoResizeIndex = null;
					_groupReduceOrderLocation = -1;
					_automaticResizeOrder.Clear();
					_groupReductionResizeStatus.Clear();
					break;
				case NotifyCollectionChangedAction.Remove:
					if (GroupSizeReductionOrder != null)
					{
						int removedCount = e.OldItems.Count;
						for (int i = 0; i < removedCount; i++)
						{
							int deletedItemIndex = i + e.OldStartingIndex;
							for (int index = 0; index < _automaticResizeOrder.Count; index++)
							{
								if (deletedItemIndex == _automaticResizeOrder[index])
								{
									_automaticResizeOrder.RemoveAt(index--);
								}
								else if (deletedItemIndex < _automaticResizeOrder[index])
								{
									_automaticResizeOrder[index]--;
								}
							}

							if (_groupAutoResizeIndex != null &&
								_groupAutoResizeIndex.Value > deletedItemIndex)
							{
								_groupAutoResizeIndex--;

								// If we have underflowed our Groups collection, start again at the end.  This
								// is what makes our group reduction cyclical.
								if (_groupAutoResizeIndex < 0)
								{
									_groupAutoResizeIndex = Items.Count - 1;
								}
							}
						}
					}
					break;
			}
		}

		protected override void OnKeyDown(KeyEventArgs e)
		{
			base.OnKeyDown(e);

			if (!e.Handled)
			{
				DependencyObject focusedElement = Keyboard.FocusedElement as DependencyObject;
				RibbonTabHeader tabHeader = RibbonTabHeader;
				if (e.Key == Key.Up && focusedElement != null && tabHeader != null)
				{
					// On arrow up key press if the focus goes out of the tab,
					// then force it to move to the corresponding TabHeader.
					DependencyObject upObj = RibbonHelper.PredictFocus(focusedElement, FocusNavigationDirection.Up);
					if (!RibbonHelper.IsAncestorOf(this, upObj))
					{
						if (tabHeader.Focus())
						{
							e.Handled = true;
						}
					}
				}
			}
		}

		#endregion

		#region Resizing Logic

		/// <summary>
		///     Method which finds the next group to
		///     increase size of and increases it.
		/// </summary>
		internal bool IncreaseNextGroupSize()
		{
			RibbonGroup nextGroup = null;
			return IncreaseNextGroupSize(true, out nextGroup);
		}

		/// <summary>
		///     Method which finds the next group to 
		///     increase the size of.
		/// </summary>
		internal RibbonGroup GetNextIncreaseSizeGroup()
		{
			RibbonGroup nextGroup = null;
			IncreaseNextGroupSize(false, out nextGroup);
			return nextGroup;
		}

		/// <summary>
		///     If the application developer has specified a GroupSizeReductionOrder, this
		///     takes the next group in that order and tells it to increase to its next size.
		///     If no GroupSizeReductionOrder was specified, or if we collapsed RibbonGroups
		///     beyond what was specified by the developer, we expand groups in reverse order
		///     of their reduction.
		/// </summary>
		/// <returns>True if a group was able to be expanded in size, false otherwise.</returns>
		private bool IncreaseNextGroupSize(bool update, out RibbonGroup nextRibbonGroup)
		{
			nextRibbonGroup = null;
			bool resizeSuccessful = false;
			int automaticResizeOrderCount = _automaticResizeOrder.Count;
			while (automaticResizeOrderCount > 0 && !resizeSuccessful)
			{
				int nextGroupIndex = _automaticResizeOrder[automaticResizeOrderCount - 1];
				nextRibbonGroup = ItemContainerGenerator.ContainerFromIndex(nextGroupIndex) as RibbonGroup;
				if (nextRibbonGroup != null)
				{
					resizeSuccessful = nextRibbonGroup.IncreaseGroupSize(update);
				}
				if (update)
				{
					_automaticResizeOrder.RemoveAt(automaticResizeOrderCount - 1);
					_groupAutoResizeIndex = nextGroupIndex;
				}
				automaticResizeOrderCount--;
			}

			if (!resizeSuccessful)
			{
				if (GroupSizeReductionOrder != null &&
					_groupReduceOrderLocation >= 0)
				{
					int groupReduceOrderLocation = _groupReduceOrderLocation;
					int resizeStatusCount = _groupReductionResizeStatus.Count;
					while (groupReduceOrderLocation >= 0 && !resizeSuccessful)
					{
						Debug.Assert(resizeStatusCount > 0);
						bool wasResizeSuccessful = _groupReductionResizeStatus[resizeStatusCount - 1];
						if (update)
						{
							_groupReductionResizeStatus.RemoveAt(resizeStatusCount - 1);
						}
						resizeStatusCount--;
						if (!wasResizeSuccessful)
						{
							groupReduceOrderLocation--;
							continue;
						}

						// Find the RibbonGroup whose name is specified next in the GroupSizeReductionOrder.
						nextRibbonGroup = FindRibbonGroupWithName(GroupSizeReductionOrder[groupReduceOrderLocation--]);

						if (nextRibbonGroup == null)
						{
							resizeSuccessful = false;
						}
						else
						{
							// A group was found, tell it to increase its size.
							resizeSuccessful = nextRibbonGroup.IncreaseGroupSize(update);
						}
					}
					if (update)
					{
						_groupReduceOrderLocation = groupReduceOrderLocation;
					}
				}
			}

			if (!resizeSuccessful)
			{
				nextRibbonGroup = null;
			}
			return resizeSuccessful;
		}

		/// <summary>
		///     If the application developer has specified a GroupSizeReductionOrder, this
		///     takes the next group in that order and tells it to reduce to its next size.
		///     If no GroupSizeReductionOrder was specified, or if we need to collapse RibbonGroups
		///     beyond what was specified by the developer, we reduce groups from right-to-left,
		///     step by step in cyclical order.
		/// </summary>
		/// <returns>
		///     Returns true if a group was located and resized successfully, false otherwise.
		/// </returns>
		internal bool DecreaseNextGroupSize()
		{
			bool resizeSuccessful = false;
			if (GroupSizeReductionOrder != null)
			{
				while (_groupReduceOrderLocation < GroupSizeReductionOrder.Count - 1 && !resizeSuccessful)
				{
					// Find the group who's next to be reduced.
					RibbonGroup targetGroup = FindRibbonGroupWithName(GroupSizeReductionOrder[++_groupReduceOrderLocation]);

					if (targetGroup == null)
					{
						resizeSuccessful = false;
					}
					else
					{
						resizeSuccessful = targetGroup.DecreaseGroupSize();
					}
					_groupReductionResizeStatus.Add(resizeSuccessful);
				}
			}

			if (!resizeSuccessful)
			{
				// Either no GroupSizeReductionOrder was specified, or we've run out of predefined orderings.
				// In this case we should begin reducing groups in size right-to-left, step by step, in cyclical
				// order.
				resizeSuccessful = DefaultCyclicalReduceGroup();
			}

			return resizeSuccessful;
		}

		/// <summary>
		///     From right-to-left, finds the next group who can be reduced in size.  If the leftmost
		///     group is encountered reduction will continue in a cyclical fashion back at the rightmost
		///     RibbonGroup.
		/// </summary>
		/// <returns>True if a group was successfully located and reduced in size, false otherwise.</returns>
		private bool DefaultCyclicalReduceGroup()
		{
			bool resizeSuccessful = false;

			if (_groupAutoResizeIndex == null)
			{
				_groupAutoResizeIndex = Items.Count - 1;
			}

			bool resizesRemain = true;

			while (resizesRemain && !resizeSuccessful)
			{
				int numAttempts = 0;
				do
				{
					numAttempts++;
					RibbonGroup group = ItemContainerGenerator.ContainerFromIndex((_groupAutoResizeIndex--).Value) as RibbonGroup;
					if (group != null)
					{
						resizeSuccessful = group.DecreaseGroupSize();
					}

					if (resizeSuccessful == true)
					{
						_automaticResizeOrder.Add(_groupAutoResizeIndex.Value + 1);
					}

					// If we have underflowed our Groups collection, start again at the end.  This
					// is what makes our group reduction cyclical.
					if (_groupAutoResizeIndex.Value < 0)
					{
						_groupAutoResizeIndex = Items.Count - 1;
						break;
					}
				} while (resizeSuccessful == false);

				// If we failed to resize during this pass, and we attempted to resize for every
				// group, then there are no reamining groups to resize.
				if (numAttempts == Items.Count)
				{
					resizesRemain = false;
				}
			}

			return resizeSuccessful;
		}
		#endregion

		#region Automation

		protected override AutomationPeer OnCreateAutomationPeer()
		{
			return new RibbonTabAutomationPeer(this);
		}

		#endregion

		#region Internal Methods

		internal void PrepareRibbonTab()
		{
			if (ContextualTabGroupHeader != null && Ribbon.ContextualTabGroupItemsControl != null)
			{
				ContextualTabGroup = Ribbon.ContextualTabGroupItemsControl.FindHeader(ContextualTabGroupHeader);
			}

			CoerceValue(VisibilityProperty);

			RibbonTabHeader tabHeader = RibbonTabHeader;
			if (tabHeader != null)
			{
				tabHeader.InitializeTransferProperties();
			}
		}

		internal void NotifyPropertyChanged(DependencyPropertyChangedEventArgs e)
		{
			if (e.Property == HeaderStyleProperty || e.Property == Ribbon.TabHeaderStyleProperty)
			{
				PropertyHelper.TransferProperty(this, HeaderStyleProperty);
			}
			else if (e.Property == HeaderTemplateProperty || e.Property == Ribbon.TabHeaderTemplateProperty)
			{
				PropertyHelper.TransferProperty(this, HeaderTemplateProperty);
			}
		}

		#endregion

		#region Private Methods

		private static object CoerceVisibility(DependencyObject d, object value)
		{
			Visibility baseVisibility = (Visibility)value;
			Visibility contextualVisibility = Visibility.Visible;
			RibbonTab tab = (RibbonTab)d;
			bool contextualHeaderSet = tab.ContextualTabGroupHeader != null;

			if (tab.ContextualTabGroup == null && contextualHeaderSet)
			{
				if (tab.Ribbon != null && tab.Ribbon.ContextualTabGroupItemsControl != null)
				{
					tab.ContextualTabGroup = tab.Ribbon.ContextualTabGroupItemsControl.FindHeader(tab.ContextualTabGroupHeader);
				}
			}

			if (tab.ContextualTabGroup != null)
			{
				contextualVisibility = tab.ContextualTabGroup.Visibility;
			}
			else if (contextualHeaderSet)
			{
				contextualVisibility = Visibility.Collapsed;
			}

			if (baseVisibility != Visibility.Visible ||
				contextualVisibility != Visibility.Visible)
			{
				return Visibility.Collapsed;
			}
			else
			{
				return Visibility.Visible;
			}
		}

		private static void OnVisibilityChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
		{
			RibbonTab tab = (RibbonTab)sender;
			if (tab.RibbonTabHeader != null)
			{
				tab.RibbonTabHeader.CoerceValue(VisibilityProperty);
			}

			// If the selected tab goes from visible to no longer visible, then reset the Ribbon's selected tab.
			Ribbon ribbon = tab.Ribbon;
			if (ribbon != null &&
				tab.IsSelected &&
				(Visibility)e.OldValue == Visibility.Visible &&
				(Visibility)e.NewValue != Visibility.Visible)
			{
				ribbon.ResetSelection();
			}
		}

		/// <summary>
		///     Property changed callback for Header property
		/// </summary>
		private static void OnHeaderChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			RibbonTab tab = (RibbonTab)d;
			Ribbon ribbon = tab.Ribbon;
			if (ribbon != null)
			{
				ribbon.NotifyTabHeaderChanged();
			}
			OnNotifyHeaderPropertyChanged(d, e);
		}

		/// <summary>
		///     Property changed called back for IsSelected property
		/// </summary>
		private static void OnIsSelectedChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
		{
			RibbonTab ribbonTab = (RibbonTab)sender;
			if (ribbonTab.IsSelected)
			{
				ribbonTab.OnSelected(new RoutedEventArgs(Selector.SelectedEvent, ribbonTab));
			}
			else
			{
				ribbonTab.OnUnselected(new RoutedEventArgs(Selector.UnselectedEvent, ribbonTab));
			}
			RibbonTabHeader header = ribbonTab.RibbonTabHeader;
			if (header != null)
			{
				header.CoerceValue(RibbonTabHeader.IsRibbonTabSelectedProperty);
			}

			// Raise UI automation events on this RibbonTab
			if (AutomationPeer.ListenerExists(AutomationEvents.SelectionItemPatternOnElementSelected)
				|| AutomationPeer.ListenerExists(AutomationEvents.SelectionItemPatternOnElementRemovedFromSelection))
			{
				RibbonTabAutomationPeer peer = RibbonTabAutomationPeer.CreatePeerForElement(ribbonTab) as RibbonTabAutomationPeer;
				if (peer != null)
				{
					peer.RaiseTabSelectionEvents();
				}
			}
		}

		/// <summary>
		///     Property changed callback for IsEnabled property.
		/// <summary>
		private static void OnIsEnabledChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
		{
			RibbonTab ribbonTab = (RibbonTab)sender;
			RibbonTabHeader header = ribbonTab.RibbonTabHeader;
			if (header != null)
			{
				header.CoerceValue(RibbonTabHeader.IsEnabledProperty);
			}
		}

		/// <summary>
		///     Property changed callback for ContextualTabGroupHeader property
		/// </summary>
		private static void OnContextualTabGroupHeaderChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			RibbonTab ribbonTab = (RibbonTab)d;
			Ribbon ribbon = ribbonTab.Ribbon;
			if (ribbon != null)
			{
				ribbon.NotifyTabContextualTabGroupHeaderChanged();
				if (e.NewValue != null && ribbonTab.Ribbon.ContextualTabGroupItemsControl != null)
				{
					ribbonTab.ContextualTabGroup = ribbonTab.Ribbon.ContextualTabGroupItemsControl.FindHeader(e.NewValue);
				}
				else
				{
					ribbonTab.ContextualTabGroup = null;
				}
				OnNotifyHeaderPropertyChanged(d, e);

				ribbonTab.CoerceValue(VisibilityProperty);
			}
		}

		private RibbonGroup FindRibbonGroupWithName(string groupName)
		{
			if (groupName != null)
			{
				groupName = groupName.Trim();
			}
			if (string.IsNullOrEmpty(groupName))
			{
				return null;
			}
			int itemCount = Items.Count;
			for (int i = 0; i < itemCount; i++)
			{
				RibbonGroup group = ItemContainerGenerator.ContainerFromIndex(i) as RibbonGroup;
				if (group != null && group.Name == groupName)
				{
					return group;
				}
			}
			return null;
		}

		private static void OnNotifyHeaderPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			RibbonTab tab = (RibbonTab)d;
			tab.NotifyPropertyChanged(e);
			RibbonTabHeader tabHeader = tab.RibbonTabHeader;
			if (tabHeader != null)
			{
				tabHeader.NotifyPropertyChanged(e);
			}
		}

		private static object CoerceHeaderStyle(DependencyObject d, object baseValue)
		{
			RibbonTab ribbonTab = (RibbonTab)d;
			return PropertyHelper.GetCoercedTransferPropertyValue(ribbonTab,
				baseValue,
				HeaderStyleProperty,
				ribbonTab.Ribbon,
				Ribbon.TabHeaderStyleProperty);
		}

		private static object CoerceHeaderTemplate(DependencyObject d, object baseValue)
		{
			RibbonTab ribbonTab = (RibbonTab)d;
			return PropertyHelper.GetCoercedTransferPropertyValue(ribbonTab,
				baseValue,
				HeaderTemplateProperty,
				ribbonTab.Ribbon,
				Ribbon.TabHeaderTemplateProperty);
		}

		#endregion

		#region Private Data

		private int _groupReduceOrderLocation = -1;
		private int? _groupAutoResizeIndex;
		private Collection<int> _automaticResizeOrder = new Collection<int>();
		private Collection<bool> _groupReductionResizeStatus = new Collection<bool>();

		#endregion

		#region KeyTips

		private static void OnKeyTipChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			RibbonTab tab = (RibbonTab)d;
			RibbonTabHeader tabHeader = tab.RibbonTabHeader;
			if (tabHeader != null)
			{
				tabHeader.CoerceValue(KeyTipService.KeyTipProperty);
			}
		}

		/// <summary>
		///     DependencyProperty for KeyTip property.
		/// </summary>
		public static readonly DependencyProperty KeyTipProperty =
			KeyTipService.KeyTipProperty.AddOwner(typeof(RibbonTab));

		/// <summary>
		///     KeyTip string for the control.
		/// </summary>
		public string KeyTip
		{
			get { return KeyTipService.GetKeyTip(this); }
			set { KeyTipService.SetKeyTip(this, value); }
		}

		private static void OnActivatingKeyTipThunk(object sender, ActivatingKeyTipEventArgs e)
		{
			((RibbonTab)sender).OnActivatingKeyTip(e);
		}

		protected virtual void OnActivatingKeyTip(ActivatingKeyTipEventArgs e)
		{
			if (e.OriginalSource == this)
			{
				// Disable the keytip. The KeyTip is
				// actually used by RibbonTabHeader and hence
				// that will take care of this.
				e.KeyTipVisibility = Visibility.Collapsed;
			}
		}

		private static void OnKeyTipAccessedThunk(object sender, KeyTipAccessedEventArgs e)
		{
			((RibbonTab)sender).OnKeyTipAccessed(e);
		}

		protected virtual void OnKeyTipAccessed(KeyTipAccessedEventArgs e)
		{
		}

		#endregion
	}
}
